package cn.piesat.service.impl;

import cn.piesat.mapper.ResourceMapper;
import cn.piesat.util.JedisUtils;
import cn.piesat.util.PageUtil;
import cn.piesat.vo.resource.*;
import cn.piesat.dao.PiesatResourceInfoDAO;
import cn.piesat.dao.PiesatRoleResourceDAO;
import cn.piesat.dto.PiesatResourceInfoDTO;
import cn.piesat.service.intf.ResourceService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 资源信息服务层逻辑实现
 *
 * @author xhl
 */
@Service
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private PiesatResourceInfoDAO piesatResourceInfoDAO;

    @Autowired
    private PiesatRoleResourceDAO piesatRoleResourceDAO;

    @Override
    public Map<String, String> add(AddResourceVO dto) {
        String type = dto.getType();
        Map<String,String> resultMap = new HashMap<>();
        //同一个菜单下的资源名称不能重复
        PiesatResourceInfoDTO existResourceName = piesatResourceInfoDAO
                .findByMenuIdAndNameAndType(dto.getMenuId(), dto.getName(), type);
        if (existResourceName != null) {
            resultMap.put("success","false");
            resultMap.put("message","名称已存在");
            return resultMap;
        }
        PiesatResourceInfoDTO existResource = piesatResourceInfoDAO
                .findByResourceAndType(dto.getResource(), type);
        if (existResource != null) {
            resultMap.put("success","false");
            resultMap.put("message","资源标识在全局已存在");
            return resultMap;
        }
        if ("interface".equals(type)) {
            Jedis jedis = JedisUtils.getJedis();
            try {
                if (jedis != null) {
                    if (dto.getMenuId() == null) {
                        jedis.hset("auth:whiteList",dto.getResource(),dto.getName());
                    }
                    jedis.hset("auth:statusList",dto.getResource(),dto.getName());
                }
            } finally {
                JedisUtils.close(jedis);
            }
        }
        PiesatResourceInfoDTO resourceDO = ResourceMapper.INSTANCE.addResourceDtoToResourceDo(dto);
        resourceDO.setCreateTime(new Date());
        resourceDO.setStatus(0);
        resourceDO.setLogStatus(0);
        piesatResourceInfoDAO.save(resourceDO);
        resultMap.put("success","true");
        resultMap.put("message","添加成功");
        return resultMap;
    }

    @Override
    public Map<String, String> update(UpdateResourceVO dto) {
        String type = dto.getType();
        Map<String,String> resultMap = new HashMap<>();
        //同一个菜单下的资源名称不能重复
        PiesatResourceInfoDTO existResourceName = piesatResourceInfoDAO
                .findByMenuIdAndNameAndType(dto.getMenuId(), dto.getName(), type);
        if (existResourceName != null && !existResourceName.getId().equals(dto.getId())) {
            resultMap.put("success","false");
            resultMap.put("message","名称已存在");
            return resultMap;
        }
        PiesatResourceInfoDTO existResource = piesatResourceInfoDAO
                .findByResourceAndType(dto.getResource(), type);
        if (existResource != null && !existResource.getId().equals(dto.getId())) {
            resultMap.put("success","false");
            resultMap.put("message","资源标识在全局已存在");
            return resultMap;
        }
        PiesatResourceInfoDTO resourceDO = ResourceMapper.INSTANCE.updateResourceDtoToResourceDo(dto);
        Optional<PiesatResourceInfoDTO> optional = piesatResourceInfoDAO.findById(resourceDO.getId());
        if (optional.isPresent()) {
            String resource = optional.get().getResource();
            if ("interface".equals(resourceDO.getType())) {
                Jedis jedis = JedisUtils.getJedis();
                try {
                    if (jedis != null) {
                        //修改是白名单redis
                        if (resourceDO.getMenuId() == null) {
                            Map<String, String> whiteMap = jedis.hgetAll("auth:whiteList");
                            whiteMap.remove(resource);
                            whiteMap.put(resourceDO.getResource(),resourceDO.getName());
                            jedis.hmset("auth:whiteList",whiteMap);
                        } else {
                            String value = jedis.hget("auth:whiteList", resource);
                            if (StringUtils.isNotBlank(value)) {
                                jedis.hdel("auth:whiteList",resource);
                                jedis.hset("auth:whiteList",resourceDO.getResource(),value);
                            }
                        }
                        //修改是否打印日志状态redis
                        String value = jedis.hget("auth:statusList",resource);
                        if(StringUtils.isNotBlank(value)){
                            jedis.hdel("auth:statusList",resource);
                            jedis.hset("auth:statusList",resourceDO.getResource(),value);
                        }
                        //修改角色绑定权限redis
                        String roleValue = jedis.hget("auth:resourceRoles",resource);
                        if(StringUtils.isNotBlank(value)){
                            jedis.hdel("auth:resourceRoles",resource);
                            jedis.hset("auth:resourceRoles",resourceDO.getResource(),value);
                        }

                    }
                } finally {
                    JedisUtils.close(jedis);
                }
            }
        }
        piesatResourceInfoDAO.save(resourceDO);
        resultMap.put("success","true");
        resultMap.put("message","修改成功");
        return resultMap;
    }

    @Override
    public Map<String,Object> list(Pageable pageable, ResourceSearchVO searchDTO) {
        Page<PiesatResourceInfoDTO> page = piesatResourceInfoDAO.findAll(new Specification<PiesatResourceInfoDTO>() {
            @Override
            public Predicate toPredicate(Root<PiesatResourceInfoDTO> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                String name = searchDTO.getName();
                if (StringUtils.isNotBlank(name)) {
                    name = "%" + name + "%";
                    predicates.add(criteriaBuilder.like(root.get("name"),name));
                }
                String menuId = searchDTO.getMenuId();
                if (menuId == null) {
                    predicates.add(criteriaBuilder.isNull(root.get("menuId")));
                } else {
                    if (!"".equals(menuId)) {
                        predicates.add(criteriaBuilder.equal(root.get("menuId"),menuId));
                    }
                }
                String type = searchDTO.getType();
                if (StringUtils.isNotBlank(type)) {
                    predicates.add(criteriaBuilder.equal(root.get("type"),type));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        },pageable);
        Page<ResourceVO> resultPage = page.map(ResourceMapper.INSTANCE::resourceDoToResourceVo);
        return PageUtil.getPage(resultPage);
    }

    @Override
    public ResourceDetailVO getOne(String id) {
        return piesatResourceInfoDAO.findById(id)
                .map(ResourceMapper.INSTANCE::resourceDoToResourceDetailVo)
                .orElse(null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOne(String id) {
        //删除资源信息
        Optional<PiesatResourceInfoDTO> optional = piesatResourceInfoDAO.findById(id);
        if (optional.isPresent()) {
            PiesatResourceInfoDTO resourceInfo = optional.get();
            piesatResourceInfoDAO.deleteById(id);
            //删除资源和角色绑定关系信息
            piesatRoleResourceDAO.deleteAllByResourceId(id);
            //如果为路由，删除资源和角色在redis中的关系
            if ("interface".equals(resourceInfo.getType())) {
                Jedis jedis = JedisUtils.getJedis();
                try {
                    if (jedis != null) {
                        if (resourceInfo.getMenuId() == null) {
                            jedis.hdel("auth:whiteList",resourceInfo.getResource());
                        } else {
                            jedis.hdel("auth:resourceRoles",resourceInfo.getResource());
                        }
                        jedis.hdel("auth:statusList",resourceInfo.getResource());
                    }
                } finally {
                    JedisUtils.close(jedis);
                }
            }

        }
    }

    @Override
    public Map<String,String> findByMenuId() {
        List<PiesatResourceInfoDTO> dataList = piesatResourceInfoDAO.findAllByMenuIdIsNullAndType("interface");
        return dataList.stream()
                .collect(Collectors.toMap(PiesatResourceInfoDTO::getResource,PiesatResourceInfoDTO::getName));
    }

    @Override
    public void lock(LockResourceVO lockResourceVO) {
        String id = lockResourceVO.getId();
        Integer lock = lockResourceVO.getLock();
        Optional<PiesatResourceInfoDTO> optional = piesatResourceInfoDAO.findById(id);
        if (optional.isPresent()) {
            PiesatResourceInfoDTO resourceInfoDTO = optional.get();
            resourceInfoDTO.setStatus(lock);
            piesatResourceInfoDAO.save(resourceInfoDTO);
            Jedis jedis = JedisUtils.getJedis();
            try {
                if (jedis != null) {
                    if (lock == 0) {
                        jedis.hset("auth:whiteList",resourceInfoDTO.getResource(),resourceInfoDTO.getName());
                    } else if (lock == 1) {
                        jedis.hdel("auth:whiteList",resourceInfoDTO.getResource());
                    }
                }
            } finally {
                JedisUtils.close(jedis);
            }
        }
    }

    @Override
    public List<PiesatResourceInfoDTO> findByType(String type) {
        List<PiesatResourceInfoDTO> list = piesatResourceInfoDAO.findByType(type);
        return list;
    }

    public List<ResourceVO> listAll(ResourceSearchVO searchDTO) {
        List<PiesatResourceInfoDTO> dataList = piesatResourceInfoDAO.findAll(new Specification<PiesatResourceInfoDTO>() {
            @Override
            public Predicate toPredicate(Root<PiesatResourceInfoDTO> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                String name = searchDTO.getName();
                if (StringUtils.isNotBlank(name)) {
                    name = "%" + name + "%";
                    predicates.add(criteriaBuilder.like(root.get("name"), name));
                }
                String menuId = searchDTO.getMenuId();
                if (menuId == null) {
                    predicates.add(criteriaBuilder.isNull(root.get("menuId")));
                } else {
                    if (!"".equals(menuId)) {
                        predicates.add(criteriaBuilder.equal(root.get("menuId"), menuId));
                    }
                }
                String type = searchDTO.getType();
                if (StringUtils.isNotBlank(type)) {
                    predicates.add(criteriaBuilder.equal(root.get("type"), type));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        });
        return dataList.stream().map(ResourceMapper.INSTANCE::resourceDoToResourceVo).collect(Collectors.toList());
    }

    @Override
    public void logStatus(LockResourceVO lockResourceVO) {
        String id = lockResourceVO.getId();
        Integer lock = lockResourceVO.getLock();
        Optional<PiesatResourceInfoDTO> optional = piesatResourceInfoDAO.findById(id);
        if (optional.isPresent()) {
            PiesatResourceInfoDTO resourceInfoDTO = optional.get();
            resourceInfoDTO.setLogStatus(lock);
            piesatResourceInfoDAO.save(resourceInfoDTO);
            Jedis jedis = JedisUtils.getJedis();
            try {
                if (jedis != null) {
                    if (lock == 0) {
                        jedis.hset("auth:statusList",resourceInfoDTO.getResource(),resourceInfoDTO.getName());
                    } else if (lock == 1) {
                        jedis.hdel("auth:statusList",resourceInfoDTO.getResource());
                    }
                }
            } finally {
                JedisUtils.close(jedis);
            }
        }

    }

    @Override
    public Map<String, String> initLog() {
        return piesatResourceInfoDAO.findByTypeAndLogStatus("interface",0)
                .stream()
                .collect(Collectors.toMap(PiesatResourceInfoDTO::getResource,PiesatResourceInfoDTO::getName,(k1,k2) -> k2));
    }
}
